package com.process.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.auth.custom.LoginUserInfoHelper;
import com.auth.model.process.Process;
import com.auth.model.process.ProcessRecord;
import com.auth.model.process.ProcessTemplate;
import com.auth.model.system.SysUser;
import com.auth.service.SysUserService;
import com.auth.vo.process.ApprovalVo;
import com.auth.vo.process.ProcessFormVo;
import com.auth.vo.process.ProcessQueryVo;
import com.auth.vo.process.ProcessVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.process.mapper.OaProcessMapper;
import com.process.service.OaProcessRecordService;
import com.process.service.OaProcessService;
import com.process.service.OaProcessTemplateService;
import exception.BusinessException;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.InputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * <p>
 * 审批类型 服务实现类
 * </p>
 *
 * @author tang
 * @since 2023-07-23
 */
@Service
public class OaProcessServiceImpl extends ServiceImpl<OaProcessMapper, Process> implements OaProcessService {
    private final OaProcessMapper processMapper;
    private final RepositoryService repositoryService;
    private final SysUserService sysUserService;
    private final OaProcessTemplateService templateService;
    private final RuntimeService runtimeService;
    private final TaskService taskService;
    private final OaProcessRecordService processRecordService;
    private final HistoryService historyService;

    public OaProcessServiceImpl(OaProcessMapper processMapper,
                                RepositoryService repositoryService,
                                SysUserService sysUserService,
                                OaProcessTemplateService templateService,
                                RuntimeService runtimeService,
                                TaskService taskService,
                                OaProcessRecordService processRecordService, HistoryService historyService) {
        this.processMapper = processMapper;
        this.repositoryService = repositoryService;
        this.sysUserService = sysUserService;
        this.templateService = templateService;
        this.runtimeService = runtimeService;
        this.taskService = taskService;
        this.processRecordService = processRecordService;
        this.historyService = historyService;
    }

    /**
     * 获取分页列表
     *
     * @author Tangjiachang
     * @date 2023/7/23 16:16
     * @param processPage:
	 * @param processQueryVo:
     * @return IPage<ProcessVo>
     **/
    @Override
    public IPage<ProcessVo> selectPage(Page<ProcessVo> processPage, ProcessQueryVo processQueryVo) {
        IPage<ProcessVo> iPage = processMapper.selectPage(processPage, processQueryVo);
        return iPage;
    }

    /**
     * 部署流程
     *
     * @author Tangjiachang
     * @date 2023/7/23 19:23
     * @param deployPath:
     * @return void
     **/
    @Override
    public void deployByZipFile(String deployPath) throws BusinessException{
        InputStream resource = this.getClass()
                .getClassLoader()
                .getResourceAsStream(deployPath);
        if (resource == null) {
            throw new BusinessException(5001,"文件不存在");
        } else {
            // 部署流程
            repositoryService.createDeployment()
                    .addZipInputStream(new ZipInputStream(resource))
                    .deploy();
        }


    }

    /**
     * 启动流程实例
     *
     * @author Tangjiachang
     * @date 2023/7/23 19:23
     * @param processFormVo: 
     * @return void
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void startUp(ProcessFormVo processFormVo) {
        try {
            // 根据当前用户id获取用户信息
            // 感觉审批模版id获取模版信息
            ProcessTemplate processTemplate = templateService.getById(processFormVo.getProcessTemplateId());
            // 保存提交审批信息到业务表：oa_process中
            Process process = new Process();
            BeanUtils.copyProperties(processFormVo,process);

            // 1代表审批中
            process.setStatus(1);
            process.setProcessCode(System.currentTimeMillis() + "");
            process.setUserId(LoginUserInfoHelper.getUserId());
            process.setFormValues(processFormVo.getFormValues());
            process.setTitle(LoginUserInfoHelper.getUsername() +  "发起" + processTemplate.getName());
            baseMapper.insert(process);

            // 启动流程实例,RuntimeService
            // 流程定义key
            // 业务key processId
            String definitionKey = processTemplate.getProcessDefinitionKey();
            Long businessKey = process.getId();
            // 流程参数form表单json数据，转换map集合
            JSONObject formData = JSONObject.parseObject(processFormVo.getFormValues()).getJSONObject("formData");
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(definitionKey, String.valueOf(businessKey), new HashMap<String, Object>(1) {{
                put("data", JSONObject.toJavaObject(formData, Map.class));
            }});

            // 查询下一个审批人,审批人可能有多个
            List<String> names = new ArrayList<>(0);
            getCurrentTaskList(processInstance.getId()).stream().forEach(task -> {
                SysUser sysUser = sysUserService.getUserByUserName(task.getAssignee());
                names.add(sysUser.getName());
                //TODO 推送消息
            });

            // 将业务和流程进行关联
            process.setProcessInstanceId(processInstance.getId());
            process.setDescription("等待" + StringUtils.join(names.toArray(), ",") +"审批");
            baseMapper.updateById(process);
            processRecordService.record(process.getId(), 1, process.getTitle());
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            e.printStackTrace();
        }

    }

    /**
     * 获取待办列表
     *
     * @author Tangjiachang
     * @date 2023/7/24 21:40
     * @param processPage:
     * @return Page<Process>
     **/
    @Override
    public Page<ProcessVo> findPending(Page<Process> processPage) {
        List<ProcessVo> processes = new ArrayList<>(0);
        int begin = (int) ((processPage.getCurrent() - 1) * processPage.getSize());
        // 根据用户名称进行查询
        TaskQuery query = taskService.createTaskQuery()
                .taskAssignee(LoginUserInfoHelper.getUsername())
                .orderByTaskCreateTime()
                .desc();

        query.listPage(begin, (int) processPage.getSize())
                .forEach(task -> {
                    // 从task获取流程实例id
                    // 感觉流程实例id获取实例对象
                    // 从流程实例对象获取业务key(即processId)
                    String processId = runtimeService
                            .createProcessInstanceQuery()
                            .processInstanceId(task.getProcessInstanceId())
                            .singleResult()
                            .getBusinessKey();
                    if (StringUtils.isNotBlank(processId)) {
                        final ProcessVo processVo = new ProcessVo();
                        Process process = baseMapper.selectById(processId);
                        if (null != process) {
                            BeanUtils.copyProperties(process, processVo);
                            processVo.setTaskId(task.getId());
                            processes.add(processVo);
                        }
                    }
                });

        Page<ProcessVo> voPage = new Page<>(processPage.getCurrent(), processPage.getSize(), query.count());
        voPage.setRecords(processes);
        return voPage;
    }
    
    /**
     * 查询审批信息
     *
     * @author Tangjiachang
     * @date 2023/7/25 21:29
     * @param id: 
     * @return Map<String,Object>
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> show(Long id) {
        // 根据流程id获取流程信息
        Process process = baseMapper.selectById(id);
        // 根据流程id获取流程记录信息
        List<ProcessRecord> records = processRecordService
                .list(new LambdaQueryWrapper<ProcessRecord>()
                        .eq(ProcessRecord::getProcessId, id));
        // 根据模版id查询模版信息
        ProcessTemplate processTemplate = templateService.getById(process.getProcessTemplateId());
        // 判断当前任务是否可以审批,不能重复审批
        AtomicBoolean isApprove = new AtomicBoolean(false);
        getCurrentTaskList(process.getProcessInstanceId()).forEach( task -> {
            // 判断当前审批人是否是当前用户
            if (task.getAssignee().equals(LoginUserInfoHelper.getUsername())) {
                isApprove.set(true);
                return;
            }
        });

        return new HashMap<String, Object>(0) {{
            put("isApprove", isApprove.get());
            put("process", process);
            put("processRecordList", records);
            put("processTemplate", processTemplate);
        }};
    }

    /**
     * 审批接口
     *
     * @author Tangjiachang
     * @date 2023/7/25 21:06
     * @param approvalVo:
     * @return void
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approve(ApprovalVo approvalVo) {
        try {
            // 获取任务id,根据任务id获取流程变量
            String taskId = approvalVo.getTaskId();
            // 判断审批状态值 1代表审批通过,-1代表审批驳回
            Map<String, Object> variables = taskService.getVariables(taskId);
            String dec = null;
            if (approvalVo.getStatus() == 1) {
                taskService.complete(taskId);
                dec = "通过";
            } else {
                endTaskId(taskId);
                dec = "驳回";
            }
            // 记录审批相关过程
            processRecordService.record(approvalVo.getProcessId(),
                    approvalVo.getStatus(),
                    dec);
            // 查询上一个审批人
            Process process = baseMapper.selectById(approvalVo.getProcessId());
            // 查询任务
            List<Task> tasks = getCurrentTaskList(process.getProcessInstanceId());
            if (CollectionUtils.isNotEmpty(tasks)) {
                List<String> approves = tasks.stream()
                        .map(TaskInfo::getAssignee)
                        .collect(Collectors.toList());

                process.setDescription("等待" + StringUtils.join(approves.toArray(), ",") + "审批");
                process.setStatus(1);
            } else {
                if (approvalVo.getStatus() == 1) {
                    process.setDescription("审批完成（通过）");
                    process.setStatus(2);
                } else {
                    process.setDescription("审批完成（驳回）");
                    process.setStatus(-1);
                }
            }
            baseMapper.updateById(process);
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            throw new BusinessException(501, e.getLocalizedMessage());
        }
    }

    /**
     * 获取已处理列表
     *
     * @author Tangjiachang
     * @date 2023/7/25 21:56
     * @param processPage:
     * @return IPage<ProcessVo>
     **/
    @Override
    public IPage<ProcessVo> findProcessed(Page<Process> processPage) {
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(LoginUserInfoHelper.getUsername())
                .finished()
                .orderByTaskCreateTime()
                .desc();
        int begin = (int) ((processPage.getCurrent() - 1) * processPage.getSize());
        List<HistoricTaskInstance> listPage = query.listPage(begin, (int) processPage.getSize());
        long count = query.count();
        List<ProcessVo> processVos = new ArrayList<>(0);
        listPage.stream().forEach(page -> {
            final ProcessVo processVo = new ProcessVo();
            Process process = baseMapper.selectOne(new LambdaQueryWrapper<Process>()
                    .eq(Process::getProcessInstanceId, page.getProcessInstanceId()));
            if (null != process) {
                BeanUtils.copyProperties(process, processVo);
                processVos.add(processVo);
            }
        });
        IPage<ProcessVo> voiPage = new Page<ProcessVo>(processPage.getCurrent(),
                processPage.getSize(), count);
        voiPage.setRecords(processVos);
        return voiPage;
    }

    /**
     * 查询已发起流程
     *
     * @author Tangjiachang
     * @date 2023/7/25 22:11
     * @param processVoPage:
     * @return IPage<ProcessVo>
     **/
    @Override
    public IPage<ProcessVo> findStarted(Page<ProcessVo> processVoPage) {
        ProcessQueryVo queryVo = new ProcessQueryVo();
        queryVo.setUserId(LoginUserInfoHelper.getUserId());
        return baseMapper.selectPage(processVoPage, queryVo);
    }

    /**
     * 结束流程
     *
     * @author Tangjiachang
     * @date 2023/7/25 21:11
     * @param :
     * @return void
     **/
    public void endTaskId(String taskId) {
        // 通过taskId获取任务对象
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        // 获取流程定义模型BpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        // 获取结束流向节点
        List<EndEvent> endEvents = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        if (CollectionUtils.isEmpty(endEvents)) {
            return;
        }
        // 得到结束节点
        FlowNode endFlowNode = ((FlowNode) endEvents.get(0));
        // 当前流行节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess()
                .getFlowElement(task.getTaskDefinitionKey());
        // 临时保存原始活动方向
        List originalFlows = new ArrayList(0);
        originalFlows.addAll(currentFlowNode.getOutgoingFlows());
        // 清理当前流动方向
        currentFlowNode.getOutgoingFlows().clear();
        // 创建新流向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlow");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        // 当前节点指向流向方向
        currentFlowNode.setOutgoingFlows(Arrays.asList(newSequenceFlow));
        // 完成当前任务
        taskService.complete(taskId);
    }

    /**
     * 获取当前任务审批流程
     *
     * @author Tangjiachang
     * @date 2023/7/23 19:48
     * @param id:
     * @return List<Task>
     **/
     public List<Task> getCurrentTaskList(String id) {
       return taskService.createTaskQuery()
                .processInstanceId(id)
                .list();
     }
}
